查看原文
其他

服务网格Istio之pilot——xDS接口笔记

Tian Zhou 几米宋 2022-09-07

作者: Tian Zhou 本文转载自nino's blog。

本篇总结pilot的xDS常用接口,顺便浏览了部分pilot实现,下篇总结下istio的流量管理和服务发现的实现。简单来说istio做为管理面,集合了配置中心和服务中心两个功能,并把配置发现和服务发现以一组统一的xDS接口提供出来,数据面的envoy通过xDS获取需要的信息来做服务间通信和服务治理。

api v1 reference

Istio中部署pilot的启动方式是 pilot-discovery discovery。初始化阶段依次init了各种模块,其中discovery service就是xDS相关实现。envoy API reference 可以查到v1和v2两个版本的API文档。envoy control plane 给了v2 grpc接口相关的数据结构和接口。

pilot-xDS是几个月前0.6.0版本的环境上实验的接口,今天在0.8.0上跑发现RDS和CDS都查不到配置了,心好累。追到对应版本的代码发现因为routerule的配置升级到v1alpha3 routing API之后,APIV1只支持原来route rule配置,APIV2才支持virtual service相关配置,所以0.8.0环境上RDS查不到信息。

sDS

Tips 最初看xDS的文档的时候,非常疑惑这些接口里的service-key service-node cluster-name到底是什么含义,在0.6.0版本中有个cachestats接口,只要envoy调用过,这次查询记录就可以在cachestats中看到。本节记录每个字段的含义和查询方式。

  1. curl http://xx/v1/registration

  2. curl http://xx/v1/registration/reviews.nino.svc.cluster.local\|http

  3. curl http://xx/v1/registration/reviews.nino.svc.cluster.local\|http\|version=v2

pilot/pkg/proxy/envoy/v1/discovery.go 里v1接口文件中register函数完成了向go-restful注册服务。查询可以根据条件除了带上service-key,还可以在末尾带上labels,比如version=v2。在kube-service-registry当中service的查询是通过service endpoint 的shared informer查询的,可以很方便的匹配labels。

  1. [{

  2.   "service-key": "details.nino.svc.cluster.local|http",

  3.   "hosts": [{

  4.     "ip_address": "10.244.0.37",

  5.     "port": 9080

  6.   }]

  7.  }, {

  8.   "service-key": "reviews.nino.svc.cluster.local|http",

  9.   "hosts": [{

  10.     "ip_address": "10.244.0.38",

  11.     "port": 9080

  12.    }, {

  13.     "ip_address": "10.244.0.41",

  14.     "port": 9080

  15.    }, {

  16.     "ip_address": "10.244.0.42",

  17.     "port": 9080

  18.    }]

  19. }]

cDS

cDS的接口里的clustername和servicenode可以通过envoy启动参数配置。其中clustername可以通过serviceCluster配置,servicenode默认通过ENVOYTYPE、PODNAME、PODNAMESPACE和INSTANCEIP这些环境变量合成。

/v1/{:clustername}/{:servicenode}

  1. curl http://xx/v1/clusters/productpage/sidecar~10.244.0.40~productpage-v1-

  2. 8666ffbd7c-mf5f4.nino~nino.svc.cluster.local

  1. {

  2.  "clusters": [{

  3.    "name": "in.9080",

  4.    "connect_timeout_ms": 1000,

  5.    "type": "static",

  6.    "lb_type": "round_robin",

  7.    "hosts": [{

  8.      "url": "tcp://127.0.0.1:9080"

  9.     }]

  10.   },{

  11.    "name": "out.ratings.nino.svc.cluster.local|http|version=v1",

  12.    "service_name": "ratings.nino.svc.cluster.local|http|version=v1",

  13.    "connect_timeout_ms": 1000,

  14.    "type": "sds",

  15.    "lb_type": "round_robin"

  16.   },{

  17.    "name": "out.reviews.nino.svc.cluster.local|http|version=v1",

  18.    "service_name": "reviews.nino.svc.cluster.local|http|version=v1",

  19.    "connect_timeout_ms": 1000,

  20.    "type": "sds",

  21.    "lb_type": "round_robin"

  22.   },{

  23.    "name": "out.reviews.nino.svc.cluster.local|http|version=v2",

  24.    "service_name": "reviews.nino.svc.cluster.local|http|version=v2",

  25.    "connect_timeout_ms": 1000,

  26.    "type": "sds",

  27.    "lb_type": "round_robin"

  28.   }]

  29. }

rDS

接口类似cDS,通过istioctl创建了route rule之后可以通过routes查到结果。下面的例子是发布了关于reviews服务的权重规则。可以通过以下RDS接口查到对应的权重规则。

/v1/{:routeconfigname}/{:clustername}/{:servicenode}

  1. curl http://xx/v1/routes/9080/productpage/sidecar~10.244.0.40~

  2. productpage-v1-8666ffbd7c-mf5f4.nino~nino.svc.cluster.local

  1. {

  2.  "validate_clusters": true,

  3.  "virtual_hosts": [

  4.   {

  5.    "name": "ratings.nino.svc.cluster.local|http",

  6.    "domains": [

  7.     "ratings:9080",

  8.     "ratings",

  9.     "ratings.nino:9080",

  10.     "ratings.nino",

  11.     "ratings.nino.svc:9080",

  12.     "ratings.nino.svc",

  13.     "ratings.nino.svc.cluster:9080",

  14.     "ratings.nino.svc.cluster",

  15.     "ratings.nino.svc.cluster.local:9080",

  16.     "ratings.nino.svc.cluster.local",

  17.     "10.101.178.48:9080",

  18.     "10.101.178.48"

  19.    ],

  20.    "routes": [

  21.     {

  22.      "prefix": "/",

  23.      "cluster": "out.ratings.nino.svc.cluster.local|http|version=v1",

  24.      "timeout_ms": 0,

  25.      "decorator": {

  26.       "operation": "ratings-default"

  27.      }

  28.     }

  29.    ]

  30.   },

  31.   {

  32.    "name": "reviews.nino.svc.cluster.local|http",

  33.    "domains": [

  34.     "reviews:9080",

  35.     "reviews",

  36.     "reviews.nino:9080",

  37.     "reviews.nino",

  38.     "reviews.nino.svc:9080",

  39.     "reviews.nino.svc",

  40.     "reviews.nino.svc.cluster:9080",

  41.     "reviews.nino.svc.cluster",

  42.     "reviews.nino.svc.cluster.local:9080",

  43.     "reviews.nino.svc.cluster.local",

  44.     "10.108.121.171:9080",

  45.     "10.108.121.171"

  46.    ],

  47.    "routes": [

  48.     {

  49.      "prefix": "/",

  50.      "weighted_clusters": {

  51.       "clusters": [

  52.        {

  53.         "name": "out.reviews.nino.svc.cluster.local|http|version=v1",

  54.         "weight": 50

  55.        },

  56.        {

  57.         "name": "out.reviews.nino.svc.cluster.local|http|version=v2",

  58.         "weight": 50

  59.        }

  60.       ]

  61.      },

  62.      "timeout_ms": 0,

  63.      "decorator": {

  64.       "operation": "reviews-default"

  65.      }

  66.     }

  67.    ]

  68.   }

  69.  ]

  70. }

api v2 reference

eds http debug

虽然v2是grpc的接口,但是pilot提供了 InitDebug,可以通过debug接口查询服务和routes等服务和配置信息。比如下面是edsz的debug接口。

  1. curl http://10.99.241.12:8080/debug/edsz

  1. [{

  2.    "clusterName": "outbound|9080||reviews.nino.svc.cluster.local",

  3.    "endpoints": [{

  4.        "lbEndpoints": [{

  5.            "endpoint": {

  6.                "address": {

  7.                    "socketAddress": {

  8.                        "address": "10.244.0.56",

  9.                        "portValue": 9080

  10.                    }

  11.                }

  12.            }

  13.        }, {

  14.            "endpoint": {

  15.                "address": {

  16.                    "socketAddress": {

  17.                        "address": "10.244.0.58",

  18.                        "portValue": 9080

  19.                    }

  20.                }

  21.            }

  22.        }, {

  23.            "endpoint": {

  24.                "address": {

  25.                    "socketAddress": {

  26.                        "address": "10.244.2.25",

  27.                        "portValue": 9080

  28.                    }

  29.                }

  30.            }

  31.        }]

  32.    }]

  33. }, {

  34.    "clusterName": "outbound|9080|v3|reviews.nino.svc.cluster.local",

  35.    "endpoints": [{

  36.        "lbEndpoints": [{

  37.            "endpoint": {

  38.                "address": {

  39.                    "socketAddress": {

  40.                        "address": "10.244.0.58",

  41.                        "portValue": 9080

  42.                    }

  43.                }

  44.            }

  45.        }]

  46.    }]

  47. }]

eds grpc

在envoy的go-control-plane接口定义中istio需要实现以下接口,结果pilot只实现了StreamEndpoints。看了下基本实现eds只需要DiscoveryRequest里的ResourceNames这个字段,其实跟v1的接口一样就是需要service-key。NodeId和TypeUrl可省略,后者默认就是EndpointType。

  1. type EndpointDiscoveryServiceServer interface {

  2.    StreamEndpoints(EndpointDiscoveryService_StreamEndpointsServer) error

  3.    FetchEndpoints(context.Context, *DiscoveryRequest) (*DiscoveryResponse, error)

  4. }

  5. type EndpointDiscoveryService_StreamEndpointsServer interface {

  6.    Send(*DiscoveryResponse) error

  7.    Recv() (*DiscoveryRequest, error)

  8.    grpc.ServerStream

  9. }

这个ResourceNames支持两种格式,其中v1和version=v1可以没有,这个代表labels和新版定义的subsets。eds的解析里兼容两个版本的解析方式。

outbound|http|v1|istioserver.pilot.svc.cluster.local

istioserver.pilot.svc.cluster.local|http|version=v1

istio/pilot/pkg/proxy/envoy/v2/eds.go

  1. xds := xdsapi.NewEndpointDiscoveryServiceClient(conn)

  2. edsstr, err := xds.StreamEndpoints(context.Background())

  3. err = edsstr.Send(&xdsapi.DiscoveryRequest{

  4.    ResourceNames: []string{"istioserver.pilot.svc.cluster.local|http|version=v1"},

  5.    TypeUrl:       EndpointType,

  6. })

  7. res1, err := edsstr.Recv()

  8. cla, err := getLoadAssignment(res1)

pilot把这个响应 DiscoveryResponse的Resources字段用 ClusterLoadAssignment代替。客户端需要 getLoadAssignment从res1.Resources[0].Value反序列化出真正的响应结构。

github.com/envoyproxy/go-control-plane/envoy/api/v2/eds.pb.go

  1. type ClusterLoadAssignment struct {

  2.    ClusterName string

  3.    // List of endpoints to load balance to.

  4.    Endpoints []envoy_api_v2_endpoint.LocalityLbEndpoints

  5.    // Load balancing policy settings.

  6.    Policy *ClusterLoadAssignment_Policy

  7. }

ads grpc

查询eds还有另一种方式ads,即Aggregated Discovery Service,它封装了下面这个接口。istio在实现过程中根据typeUrl识别xDS的类别。除了eds以外,其他lds rds cds都是由ads集成的,没有单独的xds接口了。

  1. type AggregatedDiscoveryServiceServer interface {

  2.    // This is a gRPC-only API.

  3.    StreamAggregatedResources(AggregatedDiscoveryService_StreamAggregatedResourcesServer) error

  4. }

当类型选择为EndpointType时,查询结果跟eds一致,不过ads接口要求NodeID必须在pilot缓存中存在。

  1. xds := ads.NewAggregatedDiscoveryServiceClient(conn)

  2. adser, err := xds.StreamAggregatedResources(context.Background())

  3. err = adser.Send(&xdsapi.DiscoveryRequest{

  4.    Node: &envoy_api_v2_core1.Node{

  5.        Id: "sidecar~10.109.11.31~productpage-v1-586c4486b7-rk2rh.nino~nino.svc.cluster.local",

  6.    },

  7.    ResourceNames: []string{"istioserver.pilot.svc.cluster.local|http"},

  8.    TypeUrl:       EndpointType,

  9. })

  10. res1, err := adser.Recv()

  11. cla, err := getLoadAssignment(res1)

cds http debug

通过cdsz可以看到每个nodeID对应的clusters的配置,如果系统中用户没有发布subnets,从cds实际上查不到带tag或者说带version的信息。

curl http://10.99.241.12:8080/debug/cdsz

  1. {

  2.  "name": "outbound|8084||istioserver.pilot.svc.cluster.local",

  3.  "type": "EDS",

  4.  "edsClusterConfig": {

  5.    "edsConfig": {

  6.      "ads": {

  7.      }

  8.    },

  9.    "serviceName": "outbound|8084||istioserver.pilot.svc.cluster.local"

  10.  },

  11.  "connectTimeout": "1.000s",

  12.  "circuitBreakers": {

  13.    "thresholds": [

  14.      {

  15.      }

  16.    ]

  17.  }

  18. },

cds grpc

cds接口返回的数据是Cluster,包含了所有这个服务的配置信息。但因为目前用不到就布列出来了,具体定义见文件 github.com/envoyproxy/go-control-plane/envoy/api/v2/cds.pb.go

  1. type Cluster struct {

  2.    Name string

  3.    ...

  4.    Type Cluster_DiscoveryType

  5.    EdsClusterConfig *Cluster_EdsClusterConfig

  6.    ConnectTimeout time.Duration

  7.    LbPolicy Cluster_LbPolicy

  8.    CircuitBreakers *envoy_api_v2_cluster.CircuitBreakers

  9.    ...

  10. }

rds grpc

rds接口返回的数据结构是 RouteConfiguration,这个RouteConfiguration对应的是新版本的路由配置格式,就是virtual service和destination rules这对规则。

github.com/envoyproxy/go-control-plane/envoy/api/v2/rds.pb.go

  1. type RouteConfiguration struct {

  2.    Name string

  3.    VirtualHosts []envoy_api_v2_route.VirtualHost

  4.    InternalOnlyHeaders []string

  5.    ResponseHeadersToAdd []*envoy_api_v2_core1.HeaderValueOption

  6.    ResponseHeadersToRemove []string

  7.    RequestHeadersToAdd []*envoy_api_v2_core1.HeaderValueOption

  8.    ValidateClusters *google_protobuf.BoolValue

  9. }

看了数据结构注释,在RDS当中ResourceName填的是routeConfiguration的名字。

  1. rds.Send(&xdsapi.DiscoveryRequest{

  2.    ResponseNonce: time.Now().String(),

  3.    Node: &envoy_api_v2_core1.Node{

  4.        Id: "sidecar~10.244.0.58~reviews-v3-79fb5c99d5-d9k7b.nino~nino.svc.cluster.local",

  5.    },

  6.    ResourceNames: []string{"9080"},

  7.    TypeUrl:       RouteType})

rds的查询结果实例如下,我就是发布了一个reviews的50%:50%的权重规则。

  1. {

  2. "versionInfo": "2018-06-21 07:23:37.290525595 +0000 UTC m=+600.138437218",

  3. "resources": [

  4.  {

  5.   "@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",

  6.   "name": "9080",

  7.   "virtualHosts": [

  8.    {

  9.     "name": "productpage.nino.svc.cluster.local:9080",

  10.     "domains": [

  11.      "productpage.nino.svc.cluster.local",

  12.      "productpage.nino.svc.cluster.local:9080",

  13.      "productpage",

  14.      "productpage:9080",

  15.      "productpage.nino.svc.cluster",

  16.      "productpage.nino.svc.cluster:9080",

  17.      "productpage.nino.svc",

  18.      "productpage.nino.svc:9080",

  19.      "productpage.nino",

  20.      "productpage.nino:9080",

  21.      "10.109.11.31",

  22.      "10.109.11.31:9080"

  23.     ],

  24.     "routes": [

  25.      {

  26.       "match": {

  27.        "prefix": "/"

  28.       },

  29.       "route": {

  30.        "cluster": "outbound|9080|v1|productpage.nino.svc.cluster.local",

  31.        "useWebsocket": false

  32.       },

  33.       "decorator": {

  34.        "operation": "productpage"

  35.       }

  36.      }

  37.     ]

  38.    },

  39.    {

  40.     "name": "reviews.nino.svc.cluster.local:9080",

  41.     "domains": [

  42.      "reviews.nino.svc.cluster.local",

  43.      "reviews.nino.svc.cluster.local:9080",

  44.      "reviews",

  45.      "reviews:9080",

  46.      "reviews.nino.svc.cluster",

  47.      "reviews.nino.svc.cluster:9080",

  48.      "reviews.nino.svc",

  49.      "reviews.nino.svc:9080",

  50.      "reviews.nino",

  51.      "reviews.nino:9080",

  52.      "10.109.242.124",

  53.      "10.109.242.124:9080"

  54.     ],

  55.     "routes": [

  56.      {

  57.       "match": {

  58.        "prefix": "/"

  59.       },

  60.       "route": {

  61.        "weightedClusters": {

  62.         "clusters": [

  63.          {

  64.           "name": "outbound|9080|v2|reviews.nino.svc.cluster.local",

  65.           "weight": 50

  66.          },

  67.          {

  68.           "name": "outbound|9080|v3|reviews.nino.svc.cluster.local",

  69.           "weight": 50

  70.          }

  71.         ]

  72.        },

  73.        "useWebsocket": false

  74.       },

  75.       "decorator": {

  76.        "operation": "reviews"

  77.       }

  78.      }

  79.     ]

  80.    }

  81.   ],

  82.   "validateClusters": false

  83.  }

  84. ],

  85. "typeUrl": "type.googleapis.com/envoy.api.v2.RouteConfiguration",

  86. "nonce": "2018-06-21 07:23:59.18418814 +0000 UTC m=+622.032099759"

  87. }

参考

  • envoy API reference

  • envoy control plane

  • istio指南




欢迎大家踊跃投稿,投稿方式请访问:https://github.com/servicemesher/trans

Service Mesh Meetup北京站倒计时:5天

第二届Service Mesh Meetup北京站开始报名

报名名额已满,请请关注我们的公众号后续公布IT大咖说直播链接



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存